package controllers

import (
	"encoding/json"
	"fmt"
	"strings"

	"cvevulner/common"
	"cvevulner/errcode"
	"cvevulner/models"
	"cvevulner/util"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/logs"
)

// Operations about Packages

type CveDetailController struct {
	beego.Controller
}

const issueurl = "https://gitee.com/%s/%s/issues/%s"

func (c *CveDetailController) RetData(resp map[string]interface{}) {
	c.Data["json"] = resp
	c.ServeJSON()
}

func (c *CveDetailController) failed(err error) {
	resp := make(map[string]interface{})
	resp["code"] = -1
	resp["message"] = err.Error()
	resp["count"] = 0
	resp["data"] = nil
	c.RetData(resp)
	return
}

type respData struct {
	CveNum          string  `json:"cve_num"`
	OwnedComponent  string  `json:"owned_component"`
	Version         string  `json:"version"`
	IssueNum        string  `json:"issue_num"`
	IssueUrl        string  `json:"issue_url"`
	IssueStatus     int8    `json:"issue_status"`
	IssueStatusName string  `json:"issue_status_name"`
	Status          int8    `json:"status"`
	StatusName      string  `json:"status_name"`
	Type            string  `json:"scoring_system"`
	NvdScore        float64 `json:"nvd_score"`
	NvdVector       string  `json:"nvd_vector"`
	OpeneulerScore  float64 `json:"openeuler_score"`
	OpeneulerVector string  `json:"openeuler_vector"`
	Owner           string  `json:"owner"`
	CveUrl          string  `json:"cve_url"`
	Purl            string  `json:"purl"`
}

func (c *CveDetailController) success(data []respData) {
	resp := make(map[string]interface{})
	resp["code"] = 200
	resp["message"] = "success"
	resp["count"] = len(data)
	resp["data"] = data
	c.RetData(resp)
	return
}

// Get @Title Get cvedetail
// @Description get cvedetail
// @Param	cvenumber		type 	string	true
// @Success 200 {object} models.uploadcve
// @Failure 403 :cvenumber is err
// @router / [get]
func (c *CveDetailController) Get() {
	req := c.Ctx.Request
	addr := req.RemoteAddr
	logs.Info("Method: ", req.Method, "The ip address requested by the client: ", addr,
		",Header: ", req.Header, ",body: ", req.Body)
	resp := make(map[string]interface{})
	var cod common.CveOriginDetailData
	resp["errno"] = errcode.RecodeUnknowErr
	resp["errmsg"] = errcode.RecodeText(errcode.RecodeUnknowErr)
	resp["body"] = cod
	//defer u.RetData(resp)
	//Judge whether it is legal
	token := c.GetString("token")
	if token == "" {
		resp["errno"] = errcode.RecodeSessionErr
		resp["errmsg"] = errcode.RecodeText(errcode.RecodeSessionErr)
		resp["body"] = []ResultData{}
		logs.Error("token acquisition failed")
		c.RetData(resp)
		return
	} else {
		// Check token
		ok := models.CheckToken(token)
		if !ok {
			resp["errno"] = errcode.RecodeSessionErr
			resp["errmsg"] = errcode.RecodeText(errcode.RecodeSessionErr)
			resp["body"] = []ResultData{}
			logs.Error("token verification failed")
			c.RetData(resp)
			return
		}
	}
	cveNum := c.GetString("cveNum")
	if cveNum == "" {
		logs.Error("cveNum, Parameter error")
		resp["errno"] = errcode.RecodeParamErr
		resp["errmsg"] = errcode.RecodeText(errcode.RecodeParamErr)
		c.RetData(resp)
		return
	}
	cveType, typeError := c.GetInt64("cveType")
	if typeError != nil || cveType == 0 {
		logs.Error("cveType, Parameter error")
		resp["errno"] = errcode.RecodeParamErr
		resp["errmsg"] = errcode.RecodeText(errcode.RecodeParamErr)
		c.RetData(resp)
		return
	}
	// The original data comes from the display of the Chinese Academy of Sciences
	if cveType == 1 {
		var ou models.OriginUpstream
		ouErr := models.GetOriginUpstream(cveNum, &ou)
		if ouErr != nil || ou.CveId == 0 {
			resp["errno"] = errcode.RecodeNodata
			resp["errmsg"] = errcode.RecodeText(errcode.RecodeNodata)
			c.RetData(resp)
			return
		}
		cod.CveNum = ou.CveNum
		cod.CvePackName = ou.PackName
		cod.Title = ou.Title
		cod.CnnvdID = ou.CnnvdID
		cod.CnvdID = ou.CnvdID
		cod.PublishedDate = ou.PublishedDate
		cod.VulStatus = ou.VulStatus
		cod.GetTime = ou.FirstPerTime
		var oud models.OriginUpstreamDesc
		oudErr := models.GetOriginDesc(ou.CveId, &oud)
		if oudErr == nil && oud.DescId > 0 {
			cod.Description.EnDesc = oud.EnDescription
			cod.Description.ZhDesc = oud.ZhDescription
		}
		var ouc models.OriginUpstreamConfig
		oucErr := models.GetOriginConfig(ou.CveId, &ouc)
		if oucErr == nil && ouc.ConfId > 0 {
			var oucn []models.OriginUpstreamConfigNode
			var cf common.CveConfigurations
			oucnNum, oucnErr := models.GetOriginConfigNode(ouc.ConfId, &oucn)
			if oucnNum > 0 && oucnErr == nil {
				for _, nodes := range oucn {
					var cn common.ConfNodes
					cn.Operator = nodes.Operator
					var oucnc []models.OriginUpstreamConfigNodeCpe
					oucncNum, oucncErr := models.GetOriginConfigNodeCpe(nodes.NodeId, &oucnc)
					if oucncNum > 0 && oucncErr == nil {
						for _, cpe := range oucnc {
							var cnc common.NodeCpe
							cnc.Vulnerable = cpe.Vulnerable
							cnc.CpeMatchString = cpe.CpeMatchString
							cnc.Cpe23Uri = cpe.Cpe23Uri
							cn.Cpe = append(cn.Cpe, cnc)
						}
					}
					cf.Nodes = append(cf.Nodes, cn)
				}
			}
			cod.Configurations = cf
		}
		cveImpact, ok := models.QueryCveImpact(ou.CveId)
		if ok && cveImpact.ImpactId > 0 {
			cveScore, ok := models.QueryCveScore(cveImpact.ImpactId, "v3")
			if ok && cveScore.ScoreId > 0 {
				cveScV3, ok := models.QueryCveCvssV3(cveScore.ScoreId)
				if ok && cveScV3.V3Id > 0 {
					logs.Info(cveScV3)
					cod.Impact.BaseMetricV3.CvssV3.Version = cveScV3.Version
					cod.Impact.BaseMetricV3.CvssV3.VectorString = cveScV3.VectorString
					cod.Impact.BaseMetricV3.CvssV3.AttackComplexity = cveScV3.AttackComplexity
					cod.Impact.BaseMetricV3.CvssV3.AttackVector = cveScV3.AttackVector
					cod.Impact.BaseMetricV3.CvssV3.AvailabilityImpact = cveScV3.AvailabilityImpact
					cod.Impact.BaseMetricV3.CvssV3.BaseSeverity = cveScV3.BaseSeverity
					cod.Impact.BaseMetricV3.CvssV3.UserInteraction = cveScV3.UserInteraction
					cod.Impact.BaseMetricV3.CvssV3.BaseScore = cveScV3.BaseScore
					cod.Impact.BaseMetricV3.CvssV3.PrivilegesRequired = cveScV3.PrivilegesRequired
					cod.Impact.BaseMetricV3.CvssV3.ConfidentialityImpact = cveScV3.ConfidentialityImpact
					cod.Impact.BaseMetricV3.CvssV3.IntegrityImpact = cveScV3.IntegrityImpact
					cod.Impact.BaseMetricV3.CvssV3.Scope = cveScV3.Scope
					cod.Impact.BaseMetricV3.ExploitabilityScore = cveScV3.ExploitabilityScore
					cod.Impact.BaseMetricV3.ImpactScore = cveScV3.ImpactScore
				}
			}
			cveScoreV2, ok2 := models.QueryCveScore(cveImpact.ImpactId, "v2")
			if ok2 && cveScoreV2.ScoreId > 0 {
				cveScV2, okV2 := models.QueryCveCvssV2(cveScoreV2.ScoreId)
				if okV2 && cveScV2.V2Id > 0 {
					logs.Info(cveScV2)
					cod.Impact.BaseMetricV2.CvssV2.VectorString = cveScV2.VectorString
					cod.Impact.BaseMetricV2.CvssV2.AccessComplexity = cveScV2.AccessComplexity
					cod.Impact.BaseMetricV2.CvssV2.AvailabilityImpact = cveScV2.AvailabilityImpact
					cod.Impact.BaseMetricV2.CvssV2.Authentication = cveScV2.Authentication
					cod.Impact.BaseMetricV2.CvssV2.Version = cveScV2.Version
					cod.Impact.BaseMetricV2.CvssV2.BaseScore = cveScV2.BaseScore
					cod.Impact.BaseMetricV2.CvssV2.IntegrityImpact = cveScV2.IntegrityImpact
					cod.Impact.BaseMetricV2.CvssV2.ConfidentialityImpact = cveScV2.ConfidentialityImpact
					cod.Impact.BaseMetricV2.CvssV2.AccessVector = cveScV2.AccessVector
					cod.Impact.BaseMetricV2.AcInsufInfo = cveScV2.AcInsufInfo
					cod.Impact.BaseMetricV2.UserInteractionRequired = cveScV2.UserInteractionRequired
					cod.Impact.BaseMetricV2.Severity = cveScV2.Severity
					cod.Impact.BaseMetricV2.ObtainUserPrivilege = cveScV2.ObtainUserPrivilege
					cod.Impact.BaseMetricV2.ObtainAllPrivilege = cveScV2.ObtainAllPrivilege
					cod.Impact.BaseMetricV2.ImpactScore = cveScV2.ImpactScore
					cod.Impact.BaseMetricV2.ExploitabilityScore = cveScV2.ExploitabilityScore
					cod.Impact.BaseMetricV2.ObtainOtherPrivilege = cveScV2.ObtainOtherPrivilege
				}
			}
		}
		var oup models.OriginUpstreamPoc
		oupErr := models.GetOriginPoc(ou.CveId, &oup)
		if oupErr == nil && oup.PocId > 0 {
			cod.Poc.Url = oup.Url
			cod.Poc.Date = oup.Date
			cod.Poc.Source = oup.Source
			cod.Poc.Path = oup.Path
			cod.Poc.Desc = oup.Desc
			cod.Poc.Dbindex = oup.Dbindex
		}
		var oue []models.OriginUpstreamEvent
		oueErr := models.GetOriginEvent(ou.CveId, &oue)
		if oueErr == nil && len(oue) > 0 {
			for _, v := range oue {
				cod.Event = append(cod.Event, common.CveEvent{
					Title:       v.Title,
					Date:        v.Date,
					Description: v.Description,
					Url:         v.Url,
				})
			}
		}
		var our []models.OriginUpstreamReference
		ourNum, ourErr := models.GetOriginReference(ou.CveId, &our)
		if ourErr == nil && ourNum > 0 {
			for _, reference := range our {
				var crd common.CveReferenceData
				crd.Url = reference.Url
				crd.Name = reference.Name
				crd.Tags = append(crd.Tags, reference.Tags)
				crd.Refsource = reference.Refsource
				crd.SourceUrl = reference.SourceUrl
				cod.ReferenceData = append(cod.ReferenceData, crd)
			}
		}
		var ouv []models.OriginUpstreamVulType
		ouvErr := models.GetOriginVulType(ou.CveId, &ouv)
		if ouvErr == nil && len(ouv) > 0 {
			for _, v := range ouv {
				cod.VulType = append(cod.VulType, common.CveVulType{
					Cwe: v.Cwe,
					En:  v.EnDesc,
					Zh:  v.ZhDesc,
				})
			}

		}
		var ouf models.OriginUpstreamFixSuggest
		oufErr := models.GetOriginFixSuggest(ou.CveId, &ouf)
		if oufErr == nil && ouf.FixId > 0 {
			var oufr []models.OriginUpstreamFixSuggestRef
			oufrNum, oufErr := models.GetOriginFixSuggestRef(ouf.FixId, &oufr)
			if oufErr == nil && oufrNum > 0 {
				for _, ref := range oufr {
					var fr common.FixReferences
					fr.Refsource = ref.Refsource
					fr.Name = ref.Name
					fr.Url = ref.Url
					var oufrt []models.OriginUpstreamFixSuggestRefTag
					oufrtNum, oufrtErr := models.GetOriginFixSuggestRefTag(ref.FixRefId, &oufrt)
					if oufrtErr == nil && oufrtNum > 0 {
						for _, tag := range oufrt {
							fr.Tags = append(fr.Tags, tag.Name)
						}
					}
					cod.FixSuggest.References = append(cod.FixSuggest.References, fr)
				}
			}
			cod.FixSuggest.Detail = ouf.Detail
		}
		patch, err := models.QueryCveOriginPatchInfo(cveNum)
		if err == nil && len(patch) > 0 {
			for _, v := range patch {
				cod.Patch = append(cod.Patch, common.CveOriginPatch{
					Package: v.Package, FixVersion: v.FixVersion, FixPatch: v.FixPatch, BreakPatch: v.BreakPatch, Source: v.Source, Branch: v.Branch,
				})
			}
		}
		resp["errno"] = errcode.RecodeOk
		resp["errmsg"] = errcode.RecodeText(errcode.RecodeOk)
		resp["body"] = cod
		c.RetData(resp)
		return
	} else {
		resp["errno"] = errcode.RecodeNodata
		resp["errmsg"] = errcode.RecodeText(errcode.RecodeNodata)
		c.RetData(resp)
		return
	}
}

// Post purl accord get cve detail
func (c *CveDetailController) Post() {
	cveRef := beego.AppConfig.String("cve::cveref")
	var req common.SbomReq
	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
	if err != nil {
		c.failed(err)
		return
	}

	var data []respData
	for _, v := range req.Coordinates {
		purl, purlerr := util.PasePurl(v)
		if purlerr != nil {
			logs.Error(fmt.Sprintf("parse purl failed, purl:%s", v))
			continue
		}
		pkg, pok := purl.GetName()
		version, vok := purl.GetVersion()
		if !pok || !vok {
			logs.Error(fmt.Sprintf("get purl failed, purl:%s", purl))
			continue
		}

		sbom, serr := models.QueryCveIssueForSbom(pkg, version)
		if serr != nil {
			logs.Error(fmt.Sprintf("sbom get issue query failed:%s", err.Error()))
			continue
		}

		for _, template := range sbom {
			data = append(data, respData{
				CveNum:          template.CveNum,
				OwnedComponent:  template.OwnedComponent,
				Version:         template.OwnedVersion,
				IssueNum:        template.IssueNum,
				IssueUrl:        fmt.Sprintf(issueurl, template.Owner, template.Repo, template.IssueNum),
				IssueStatus:     template.Status,
				IssueStatusName: c.statusName(template.Status),
				Status:          template.IssueStatus,
				StatusName:      c.issueStatusName(template.IssueStatus),
				Type:            c.scoreType(template.ScoreType),
				NvdScore:        template.NVDScore,
				NvdVector:       template.NVDVector,
				OpeneulerScore:  template.OpenEulerScore,
				OpeneulerVector: template.OpenEulerVector,
				Owner:           template.Owner,
				CveUrl:          cveRef + template.CveNum,
				Purl:            purl.Purl(),
			})
		}
	}

	c.success(data)
	return
}

func (c *CveDetailController) issueStatusName(i int8) string {
	//1:待分析；2：已正常关闭；3：已分析，待修复；4：已修复；5：已发布；6：已异常关闭"
	switch i {
	case 1:
		return "待分析"
	case 2:
		return "已正常关闭"
	case 3:
		return "已分析,待修复"
	case 4:
		return "已修复"
	case 5:
		return "已发布"
	case 6:
		return "已异常关闭"
	default:
		return "unknown"
	}
}

func (c *CveDetailController) statusName(i int8) string {
	//1:待办的；2：进行中；3：已完成；4：已拒绝；5： 已挂起; 6:已删除"`
	switch i {
	case 1:
		return "待办的"
	case 2:
		return "进行中"
	case 3:
		return "已完成"
	case 4:
		return "已拒绝"
	case 5:
		return "已挂起"
	case 6:
		return "已删除"
	default:
		return "unknown"
	}
}

func (c *CveDetailController) scoreType(typ string) string {
	switch strings.ToLower(typ) {
	case "v2":
		return "CVSS2"
	case "v3":
		return "CVSS3"
	default:
		return "CVSS2"
	}
}
